#!/usr/bin/env perl

#################################################################
# $LastChangedDate$
# $LastChangedRevision$
# $LastChangedBy$
#################################################################
use strict;
use warnings;
use Log::Log4perl qw(:easy);
use Getopt::Long;
use File::Find;
use Config::General;
use Data::Dumper;

### Declare variable
use vars qw( $logger $VERSION );
$VERSION = '2.11';
my %OPTS;
my %mF;
my %mD;
my %mM;

### SIG DIE-Handler
$SIG{__DIE__} = sub {
    if ($^S) {

        # We're in an eval {} and don't want log
        # this message but catch it later
        return;
    }
    local $Log::Log4perl::caller_depth = $Log::Log4perl::caller_depth + 1;
    LOGDIE @_;
};

### Getting the the opts from commandline
GetOptions(
    \%OPTS,           'help|h|?',        'ListPlug|LP', 'ListLogger|LL',
    'ListModules|LM', 'ModuleHelp|MH=s', 'Plug|P=s',    'Logger=s',
    'LogLevel=s',
);

### assign all required moniCADirs to @keys
my @keys = qw( MONICA_PRODUCT_DIR MONICA_TMP_DIR MONICA_ETC_DIR
  MONICA_VAR_DIR MONICA_LOG_DIR                      );

### checking required moniCADirs
foreach my $key (@keys) {
    ### check key
    die("Envirement $key is not set") if !exists $ENV{$key};
    ### replace backslash to slash
    ( $mD{$key} = $ENV{$key} ) =~ s#\\#/#g;
    ### check exists directory
    die "Envirement $key: directory $mD{$key} is not exists" if !-d $mD{$key};
}

### check base configfile for logging ###
my $BaseLoggerFile = "$mD{MONICA_ETC_DIR}/moniCA.logger";
if ( !-f $BaseLoggerFile ) {
    ### Create a BaseLoggerFile
    require moniCA::BUILD::BaseLoggingCF;
    moniCA::BUILD::BaseLoggingCF::Create($BaseLoggerFile);
}

### Gathering all moniCA files (*.plug *.logger ) in $OPTS{EtcDir};
Find_mF( $mD{MONICA_ETC_DIR}, \%mF ) or die;

### Gathering all moniCA files (*.pm ) in $OPTS{ProductDir};
Find_mM( $mD{MONICA_PRODUCT_DIR} . "/lib/modules", \%mM ) or die;

### Check flags
Help()
  if (
    $OPTS{help}
    or !(
           $OPTS{ModuleHelp}
        or $OPTS{ListPlug}
        or $OPTS{ListLogger}
        or $OPTS{ListModules}
        or $OPTS{Plug}
    )
  );
GetModuleHelp() if ( $OPTS{ModuleHelp} );

Listing( \%mF, 'plug' )   if ( $OPTS{ListPlug} );
Listing( \%mF, 'logger' ) if ( $OPTS{ListLogger} );
Listing( \%mM, 'pm' )     if ( $OPTS{ListModules} );

### check plug
if ( !exists $mF{'plug'}{ $OPTS{'Plug'} } ) {
    print 'Plug "' . $OPTS{'Plug'} . '"' . " not exists !\n";
    print "Gathering plugs with ./moniCA.pl --LP\n";
    exit 1;
}

### load configfile from plug
my $conf = new Config::General(
    -ConfigFile      => $mF{plug}{ $OPTS{'Plug'} },
    -InterPolateVars => 1,
    -InterPolateEnv  => 1
);

my %config = $conf->getall;

### assign value to $LoggerConf
my $LoggerConf = undef;
if ( exists $OPTS{Logger} and exists $mF{logger}{ $OPTS{Logger} } ) {
    $LoggerConf = $mF{logger}{ $OPTS{Logger} };
}
elsif ( exists $config{Logger} and exists $mF{logger}{ $config{Logger} } ) {
    $LoggerConf = $mF{logger}{ $config{Logger} };
}
else {
    $LoggerConf = $BaseLoggerFile;
}

### init Logging
eval {
    Log::Log4perl->init($LoggerConf);
    1;
} or die("Could not load $LoggerConf $!");
$logger = Log::Log4perl::get_logger();

### Run Plug
RunPlug( \%mF, $OPTS{Plug} ) if ( $OPTS{Plug} );

#####################################################
##  S U B
#####################################################

### Find moniCAFiles .plug and .logger
sub Find_mF {
    my $EtcDir = shift;
    my $mF     = shift;
    die("no such directory $EtcDir !") if ( !-d $EtcDir );
    ### method find (File::Find)
    find(
        sub {
            my $filename = $File::Find::name;

            if ( -f $filename and $filename =~ m#(.*).(plug|logger)$# ) {
                my $file = $1;
                my $m    = $2;
                $file =~ s#^$EtcDir/(.*)$#$1#;
                $file =~ s#/#.#g;
                $mF->{$m}{$file} = $filename;
            }
        },
        $EtcDir
    );
    return 1;
}

sub Find_mM {
    my $ProductDir = shift;
    my $mM         = shift;
    die("no such directory $ProductDir !") if ( !-d $ProductDir );
    ### method find (File::Find)
    find(
        sub {
            my $filename = $File::Find::name;
            if ( -f $filename and $filename =~ m#(.*).(pm)$# ) {
                my $file = $1;
                my $m    = $2;
                $file =~ s#^$ProductDir/(.*)$#$1#;
                $file =~ s#/#.#g;
                $mM->{$m}{$file} = $filename;
            }
        },
        $ProductDir
    );
    return 1;
}

### Listing plugs or loggers
sub Listing {
    my $mf = shift;
    my $l  = shift;
    print "\n" x 2, '=' x 55, "\n";
    print ' ' x 20, '  m o n i C A', "\n";
    print "\n Listing: ${l}s\n";
    print '=' x 55, "\n";
    foreach my $list ( sort keys %{ $mf->{$l} } ) {
        print "   $list\n";
    }
    print "\n" x 2;
    exit 1;
}

### Compile and runs plug
sub RunPlug {
    my $mF       = shift;
    my $plug     = shift;
    my $LogLevel = undef;

    ### assign value to $LogLevel
    if ( !exists $OPTS{LogLevel} ) {
        $OPTS{LogLevel} = $config{LogLevel};
    }

    ### Build basename plug as $RelModFile ( e.g. X::Y::Z => X/YZ.pm )
    ( my $RelModFile = $config{Modul} ) =~ s{ :: }{/}gxms;
    $RelModFile .= '.pm';

    ### Build package as full filename
    my $pkgfile = $mD{MONICA_PRODUCT_DIR} . '/lib/modules/' . $RelModFile;

    ### Compile PACKAGE
    eval {
        $logger->debug("load $pkgfile");
        require $pkgfile;
        my $obj = $config{Modul}->new();

        ## Setting Opts
        $obj->Plug( $OPTS{Plug} );
        $obj->PackageFile($pkgfile);
        $obj->LogLevel( $OPTS{LogLevel} );
        $obj->ProductDir( $mD{MONICA_PRODUCT_DIR} );
        $obj->EtcDir( $mD{MONICA_ETC_DIR} );
        $obj->TmpDir( $mD{MONICA_TMP_DIR} );
        $obj->VarDir( $mD{MONICA_VAR_DIR} );
        $obj->LogDir( $mD{MONICA_LOG_DIR} );
        ## setting CNF
        $obj->SetCNF( \%config );
        $obj->SetUp();
        $obj->Run();
        1;
    } or $logger->fatal("Could not load $pkgfile: $@");
}

sub GetModuleHelp {
    ( my $ModFile = $OPTS{'ModuleHelp'} ) =~ s{ \. }{/}gxms;
    $ModFile .= '.pm';
    my $pkgfile = $mD{MONICA_PRODUCT_DIR} . '/lib/modules/' . $ModFile;
    die("no such Module $OPTS{'ModuleHelp'} !") if ( !-f $pkgfile );
    require Pod::Find;
    require Pod::Usage;
    Pod::Usage::pod2usage(
        {
            -verbose => 2,
            -exitval => 2,
            -input   => $pkgfile
        }
    );
    exit;
}

### Help using POD
sub Help {
    require Pod::Usage;
    Pod::Usage::pod2usage(1);
}

### Get value from %config
### http://log4perl.sourceforge.net/releases/Log-Log4perl/docs/html/Log/Log4perl/FAQ.html#02bbc

sub GetConfig {
    my $key = shift;
    return $config{$key};
}

sub GetPlug {
    return $OPTS{Plug};
}

1;
############################ P O D ############################
__END__

=head1 NAME

sample   Using  Monica.pl

=head1 SYNOPSIS

moniCA.pl

 Options:

   --Plug|-P <plug>             run plug
  [--help|-h|-?]                print this help
  [--ListModules|-LM]           list available modules
  [--ModuleHelp|-MH <Module>]   display module help
  [--ListPlugs|-LP]             list available plugs
  [--ListLogger|-LL]            list available logger
  [--Logger <Logger>]           can listed by opt --ListLogger
  [--LogLevel DEBUG|INFO|WARN|ERROR|FATAL]


  For setting envirement moniCA in system (required dirs):

   intern moniCA        system env var
  -------------------------------------------------------------
   ProductDir           MONICA_PRODUCT_DIR
   TmpDir               MONICA_TMP_DIR
   EtcDir               MONICA_ETC_DIR
   VarDir               MONICA_VAR_DIR
   LogDir               MONICA_LOG_DIR


=cut
